package by.zmy.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

public class AopProxyFactory {

    // 创建代理对象
    public static <T> T createProxy(T target, List<Aspect> aspects) {
        Class<?> targetClass = target.getClass();
        return (T) Proxy.newProxyInstance(
                targetClass.getClassLoader(),
                targetClass.getInterfaces(),
                new AopInvocationHandler(target, aspects)
        );
    }

    // 动态代理处理器
    private static class AopInvocationHandler implements InvocationHandler {
        private final Object target;
        private final List<Aspect> aspects;

        public AopInvocationHandler(Object target, List<Aspect> aspects) {
            this.target = target;
            this.aspects = new ArrayList<>(aspects);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 创建方法调用链
            MethodInvocation invocation = () -> {
                try {
                    return method.invoke(target, args);
                } catch (Exception e) {
                    throw e.getCause();
                }
            };

            // 构建环绕通知链
            for (Aspect aspect : aspects) {
                final MethodInvocation next = invocation;
                final Aspect currentAspect = aspect;
                invocation = () -> currentAspect.around(target, method, args, next);
            }

            // 执行前置通知
            for (Aspect aspect : aspects) {
                aspect.before(target, method, args);
            }

            Object result = null;
            try {
                // 执行方法调用链
                result = invocation.proceed();
                // 执行后置通知
                for (Aspect aspect : aspects) {
                    aspect.after(target, method, args, result);
                }
                return result;
            } catch (Throwable throwable) {
                // 执行异常通知
                for (Aspect aspect : aspects) {
                    aspect.afterThrowing(target, method, args, throwable);
                }
                throw throwable;
            }
        }
    }
}
